/**
 * This file is part of the NatCrackerToolbox.
 * 
 * Copyright (C) 2009 - 2011 Peerialism Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 **/
package se.peerialism.natcrackeremu.model.traversal.logic;

import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.StringTokenizer;


import com.peerialism.natcracker.common.FilteringPolicy;
import com.peerialism.natcracker.common.GatewayDevice;
import com.peerialism.natcracker.common.PortMappingTechnique;
import com.peerialism.natcracker.common.TraversalStrategy;
import com.peerialism.natcracker.common.GatewayDevice.GatewayType;
import com.peerialism.natcracker.common.PortAllocationPolicy;
import com.peerialism.natcracker.common.PortMappingPolicy;

public class TraversalLogicPartitions {
	static boolean ENABLE_OPTIMIZATION = true;

	enum ReqKn {
		NoNeed, KnowEOrAbleToReceiveFromHost, KnowEOrAbleToReceiveFromAny, NeedE
	}

	private static ReqKn checkMapping(GatewayDevice x) {
		ReqKn ret = ReqKn.NoNeed;
		PortMappingPolicy map = x.getPortMappingPolicy();
		if (map == PortMappingPolicy.PORT_DEPENDENT)
			ret = ReqKn.KnowEOrAbleToReceiveFromHost; // Either alloc!=RND or flt!=PD
		return ret;
	}

	private static ReqKn checkFiltering(GatewayDevice x) {
		ReqKn ret = ReqKn.NoNeed;
		FilteringPolicy flt = x.getPortFilteringPolicy();
		PortAllocationPolicy alloc = x.getPortAllocationPolicy();
		if (flt == FilteringPolicy.PORT_DEPENDENT)
			ret = ReqKn.NeedE; // alloc!=RND
		else if (flt == FilteringPolicy.HOST_DEPENDENT && alloc == PortAllocationPolicy.RANDOM)
			ret = ReqKn.KnowEOrAbleToReceiveFromAny; // Either alloc!=RND or flt==EI
		return ret;
	}

	private static boolean satReq(GatewayDevice x, ReqKn reqFlt) {
		boolean ret = true;
		PortAllocationPolicy alloc = x.getPortAllocationPolicy();
		FilteringPolicy flt = x.getPortFilteringPolicy();
		if (reqFlt == ReqKn.NeedE) {
			if (alloc == PortAllocationPolicy.RANDOM)
				ret = false;
		} else if (reqFlt == ReqKn.KnowEOrAbleToReceiveFromHost) {
			if (alloc == PortAllocationPolicy.RANDOM && flt == FilteringPolicy.PORT_DEPENDENT)
				ret = false;
		} else if (reqFlt == ReqKn.KnowEOrAbleToReceiveFromAny)
			if (alloc == PortAllocationPolicy.RANDOM && (flt == FilteringPolicy.HOST_DEPENDENT || flt == FilteringPolicy.PORT_DEPENDENT))
				ret = false;
		return ret;
	}

	public static TraversalStrategy getStrategyFinal(GatewayDevice x1, GatewayDevice x2) {
		PortMappingTechnique str1 = PortMappingTechnique.NONE;
		PortMappingTechnique str2 = PortMappingTechnique.NONE;
		boolean cantConnect = true;
		boolean first = false;
		boolean second = false;
		if ((first = (x1.getPortFilteringPolicy() == FilteringPolicy.ENDPOINT_INDEPENDENT && x1.getPortMappingPolicy() == PortMappingPolicy.ENDPOINT_INDEPENDENT))
				|| (second = (x2.getPortFilteringPolicy() == FilteringPolicy.ENDPOINT_INDEPENDENT && x2.getPortMappingPolicy() == PortMappingPolicy.ENDPOINT_INDEPENDENT))) {
			// One is F=EI && M=EI
			if (first)
				str1 = PortMappingTechnique.ACQUIRE;
			else if (second)
				str2 = PortMappingTechnique.ACQUIRE;
			cantConnect = false;
		} else {
			ReqKn reqFlt1 = checkFiltering(x1);
			ReqKn reqFlt2 = checkFiltering(x2);
			ReqKn reqMap1 = checkMapping(x1);
			ReqKn reqMap2 = checkMapping(x2);
			// Take the most restrictive of req1
			ReqKn req1 = ReqKn.NoNeed;
			if (reqFlt1 == ReqKn.NeedE)
				req1 = ReqKn.NeedE;
			else if (reqFlt1 == ReqKn.KnowEOrAbleToReceiveFromAny)
				req1 = ReqKn.KnowEOrAbleToReceiveFromAny;
			else if (reqMap1 == ReqKn.KnowEOrAbleToReceiveFromHost)
				req1 = ReqKn.KnowEOrAbleToReceiveFromHost;
			// Take the most restrictive of req2
			ReqKn req2 = ReqKn.NoNeed;
			if (reqFlt2 == ReqKn.NeedE)
				req2 = ReqKn.NeedE;
			else if (reqFlt2 == ReqKn.KnowEOrAbleToReceiveFromAny)
				req2 = ReqKn.KnowEOrAbleToReceiveFromAny;
			else if (reqMap2 == ReqKn.KnowEOrAbleToReceiveFromHost)
				req2 = ReqKn.KnowEOrAbleToReceiveFromHost;
			if (satReq(x2, req1) && satReq(x1, req2)) {
				if (ENABLE_OPTIMIZATION) {
					StrategyCombination comb = decideStrategy(x1, x2);
					str1 = comb.str1;
					str2 = comb.str2;
				} else if (x1.getPortFilteringPolicy() == FilteringPolicy.ENDPOINT_INDEPENDENT && x2.getPortFilteringPolicy() != FilteringPolicy.PORT_DEPENDENT) {
					str1 = PortMappingTechnique.ACQUIRE;
					str2 = PortMappingTechnique.NONE;
				} else if (x2.getPortFilteringPolicy() == FilteringPolicy.ENDPOINT_INDEPENDENT && x1.getPortFilteringPolicy() != FilteringPolicy.PORT_DEPENDENT) {
					str1 = PortMappingTechnique.NONE;
					str2 = PortMappingTechnique.ACQUIRE;
				} else {
					str1 = k2s(x1);
					str2 = k2s(x2);
					if (x1.getPortAllocationPolicy() == PortAllocationPolicy.RANDOM)
						str1 = PortMappingTechnique.NONE;
					if (x2.getPortAllocationPolicy() == PortAllocationPolicy.RANDOM)
						str2 = PortMappingTechnique.NONE;
				}
				cantConnect = false;
			}
		}
		TraversalStrategy result;
		if (cantConnect)
			result = new TraversalStrategy(!cantConnect);
		else
			result = new TraversalStrategy(str1, str2, 0.0);
		return result;
	}

	private static StrategyCombination decideStrategy(GatewayDevice x1, GatewayDevice x2) {
		// Allocation Policy: ENDPOINT_INDEPENDENT, ENDPOINT_DEPENDENT, PORT_DEPENDENT
		PortMappingPolicy mappA = x1.getPortMappingPolicy();
		PortMappingPolicy mappB = x2.getPortMappingPolicy();
		// Filtering Policy: ENDPOINT_INDEPENDENT, ADDRESS_DEPENDENT, ADDRESS_PORT_DEPENDENT
		FilteringPolicy fltA = x1.getPortFilteringPolicy();
		FilteringPolicy fltB = x2.getPortFilteringPolicy();
		// PortMapping technique: RANDOM, PORT_PRESERVATION, PORT_CONTIGUITY
		PortAllocationPolicy allocA = x1.getPortAllocationPolicy();
		PortAllocationPolicy allocB = x2.getPortAllocationPolicy();
		StrategyCombination comb = new StrategyCombination();
		if (isPP_PP(x1, x2)) {
			// Optimize region 1 in connectivity matrix PP - PP
			// If necessary to know the endpoint, use PP
			boolean assigned = false;
			if (fltA == FilteringPolicy.PORT_DEPENDENT || mappA == PortMappingPolicy.PORT_DEPENDENT) {
				comb.str2 = k2s(x2);
				assigned = true;
			}
			if (fltB == FilteringPolicy.PORT_DEPENDENT || mappB == PortMappingPolicy.PORT_DEPENDENT) {
				comb.str1 = k2s(x1);
				assigned = true;
			}
			if (!assigned)
				// If no strategy defined
				comb.str1 = PortMappingTechnique.PREDICT_PRESERVATION;
		} else if (isPP_PC(x1, x2)) {
			// Optimize region 2 in connectivity matrix PP - PC
			boolean assigned = false;
			// If necessary to know the endpoint, use PC
			if (fltA == FilteringPolicy.PORT_DEPENDENT) {
				comb.str2 = k2s(x2);
				assigned = true;
			}
			if (fltB == FilteringPolicy.PORT_DEPENDENT) {
				comb.str1 = k2s(x1);
				assigned = true;
			}
			if (!assigned)
				// Use preservation on the side that supports it
				if (allocA == PortAllocationPolicy.PORT_PRESERVATION)
					comb.str1 = k2s(x1);
				else
					comb.str2 = k2s(x2);
		} else if (isPC_PC(x1, x2)) {
			// Optimize region 3 in connectivity matrix PP - PC
			// Filter EI, use SHP and avoid PC
			if (fltA == FilteringPolicy.ENDPOINT_INDEPENDENT && fltB != FilteringPolicy.PORT_DEPENDENT)
				comb.str1 = PortMappingTechnique.ACQUIRE;
			else if (fltB == FilteringPolicy.ENDPOINT_INDEPENDENT && fltA != FilteringPolicy.PORT_DEPENDENT)
				comb.str2 = PortMappingTechnique.ACQUIRE;
			else {
				// Filter greater than EI, we make use of contiguity in both sides to increase connectivity success (we sacrifice connection time for
				// success rate)
				comb.str1 = k2s(x1);
				comb.str2 = k2s(x2);
			}
		} else // Use preservation when possible
			if (allocA == PortAllocationPolicy.PORT_PRESERVATION)
				comb.str1 = k2s(x1);
			else if (allocB == PortAllocationPolicy.PORT_PRESERVATION)
				comb.str2 = k2s(x2);
			else
				// Use SHP when possible
				if (allocA == PortAllocationPolicy.RANDOM && fltA == FilteringPolicy.ENDPOINT_INDEPENDENT)
					comb.str1 = k2s(x1);
				else if (allocB == PortAllocationPolicy.RANDOM && fltA == FilteringPolicy.ENDPOINT_INDEPENDENT)
					comb.str2 = k2s(x2);
				else
					// Use PC if it's really needed
					if (allocA == PortAllocationPolicy.PORT_CONTIGUITY)
						comb.str1 = k2s(x1);
					else if (allocB == PortAllocationPolicy.PORT_CONTIGUITY)
						comb.str2 = k2s(x2);
		return comb;
	}

	public static boolean isPP_PP(GatewayDevice x1, GatewayDevice x2) {
		if (x1.getPortAllocationPolicy() == PortAllocationPolicy.PORT_PRESERVATION && x2.getPortAllocationPolicy() == PortAllocationPolicy.PORT_PRESERVATION)
			return true;
		return false;
	}

	public static boolean isPP_PC(GatewayDevice x1, GatewayDevice x2) {
		if (x1.getPortAllocationPolicy() == PortAllocationPolicy.PORT_PRESERVATION && x2.getPortAllocationPolicy() == PortAllocationPolicy.PORT_CONTIGUITY)
			return true;
		else if (x1.getPortAllocationPolicy() == PortAllocationPolicy.PORT_CONTIGUITY && x2.getPortAllocationPolicy() == PortAllocationPolicy.PORT_PRESERVATION)
			return true;
		return false;
	}

	public static boolean isPC_PC(GatewayDevice x1, GatewayDevice x2) {
		if (x1.getPortAllocationPolicy() == PortAllocationPolicy.PORT_CONTIGUITY && x2.getPortAllocationPolicy() == PortAllocationPolicy.PORT_CONTIGUITY)
			return true;
		return false;
	}

	private static PortMappingTechnique k2s(GatewayDevice x) {
		PortMappingTechnique str = PortMappingTechnique.NONE;
		if (x.getPortAllocationPolicy() == PortAllocationPolicy.PORT_CONTIGUITY)
			str = PortMappingTechnique.PREDICT_CONTIGUITY;
		else if (x.getPortAllocationPolicy() == PortAllocationPolicy.PORT_PRESERVATION)
			str = PortMappingTechnique.PREDICT_PRESERVATION;
		else if (x.getPortAllocationPolicy() == PortAllocationPolicy.RANDOM)
			str = PortMappingTechnique.ACQUIRE;
		return str;
	}

	public static void main(String[] args) {
		GatewayDevice x1 = new GatewayDevice();
		x1.setGatewayType(GatewayType.NAT);
		GatewayDevice x2 = new GatewayDevice();
		x2.setGatewayType(GatewayType.NAT);
		PortAllocationPolicy[] mts = PortAllocationPolicy.values();
		FilteringPolicy[] fps = FilteringPolicy.values();
		GatewayDevice[] gws = new GatewayDevice[mts.length * fps.length * PortMappingPolicy.values().length];
		int i = 0;
		for (PortAllocationPolicy mt : mts)
			for (FilteringPolicy fp : fps)
				for (PortMappingPolicy m : PortMappingPolicy.values()) {
					GatewayDevice gatewayDevice1 = new GatewayDevice();
					gatewayDevice1.setPortMappingPolicy(m);
					gatewayDevice1.setPortFilteringPolicy(fp);
					gatewayDevice1.setPortAllocationPolicy(mt);
					gatewayDevice1.setGatewayType(GatewayType.NAT);
					gws[i] = gatewayDevice1;
					i++;
				}
		FileWriter out;
		try {
			out = new FileWriter("partitions.html");

			out.write("<style type=\"text/css\"> table.sample { border-width: 1px; border-spacing: ;border-style: hidden;border-color: gray;border-collapse: separate;background-color: white; } table.sample th { border-width: 1px;padding: 1px;border-style: solid;border-color: gray;background-color: white;-moz-border-radius: ;}table.sample td {border-width: 1px;padding: 1px;border-style: solid;border-color: gray;background-color: white;-moz-border-radius: ;}</style>");

			out.write("<B><TABLE border='0'>");
			out.write("<TR>");
			out.write("<TD> </TD><TD>B</TD><TD>a</TD>");
			for (GatewayDevice gw : gws)
			{
				out.write("<TD><B>");
				out.write(removeDash(GatewayDevice.shorten(gw.getPortAllocationPolicy().name(), "_")) + "");
				out.write("</TD></B>");
			}
			out.write("</TR>");
			out.write("<TR>");
			out.write("<TD>A</TD><TD></TD><TD>f</TD>");
			for (GatewayDevice gw : gws)
			{
				out.write("<TD><B>");
				out.write(removeDash(GatewayDevice.shorten(gw.getPortFilteringPolicy().name(), "_")) + "");
				out.write("</TD></B>");
			}
			out.write("</TR>");
			out.write("<TR>");
			out.write("<TD>a</TD><TD>f</TD><TD>m</TD>");
			for (GatewayDevice gw : gws)
			{
				out.write("<TD><B>");
				out.write(removeDash(GatewayDevice.shorten(gw.getPortMappingPolicy().name(), "_")) + "");
				out.write("</TD></B>");
			}
			out.write("</TR>");
			HashSet<String> done = new HashSet<String>();
			for (int r = 0; r < gws.length; r++) {
				out.write("<TR>");
				// f.write("<TD></TD><TD></TD>");
				String aa = removeDash(GatewayDevice.shorten(gws[r].getPortAllocationPolicy().name(), "_"));
				String fa = removeDash(GatewayDevice.shorten(gws[r].getPortFilteringPolicy().name(), "_"));
				String ma = removeDash(GatewayDevice.shorten(gws[r].getPortMappingPolicy().name(), "_"));
				out.write("<TD><B>" + aa + "</B></TD>");
				out.write("<TD><B>" + fa + "</B></TD>");
				out.write("<TD><B>" + ma + "</B></TD>");
				for (int c = 0; c < gws.length; c++) {
					String ab = removeDash(GatewayDevice.shorten(gws[c].getPortAllocationPolicy().name(), "_"));
					String fb = removeDash(GatewayDevice.shorten(gws[c].getPortFilteringPolicy().name(), "_"));
					String mb = removeDash(GatewayDevice.shorten(gws[c].getPortMappingPolicy().name(), "_"));
					String cur = (c <= r) ? r + "-" + c : c + "-" + r;
					if (!done.contains(cur))
						done.add(cur);
					else {
						out.write("<TD/>");
						continue;
					}
					// TraversalStrategy s = getStrategyFinal(gws[r], gws[c]);
					String color = "black";
					String entry = colorme(s2i(fa), s2i(ma), s2i(aa), s2i(fb), s2i(mb), s2i(ab));// saa+"-"+fa+"-"+ma+"----"+ab+"-"+fb+"-"+mb;
					out.write("<TD bgcolor='" + bg(entry) + "'><font color='" + color + "'>");
					out.write("" + entry + "</font><BR/>");
					out.write("</TD>");
				}
				out.write("</TR>");
			}
			out.write("<TABLE></B>");
			out.write("total:" + total + "<BR/>");
			out.write("nSHP:" + nSHP + ":" + (nSHP / total) * 100.0 + "<BR/>");
			out.write("nP:" + nP + ":" + (nP / total) * 100.0 + "<BR/>");
			out.write("nDP:" + nDP + ":" + (nDP / total) * 100.0 + "<BR/>");
			out.write("nI:" + nI + ":" + (nI / total) * 100.0 + "<BR/>");
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	static HashMap<String, TraversalStrategy> colorToStr;

	private static TraversalStrategy genStr(String col, GatewayDevice dev1, GatewayDevice dev2) {
		colorToStr = new HashMap<String, TraversalStrategy>();
		colorToStr.put("shp(a)", str(PortMappingTechnique.ACQUIRE, PortMappingTechnique.NONE));
		colorToStr.put("shp(b)", str(PortMappingTechnique.NONE, PortMappingTechnique.ACQUIRE));
		colorToStr.put("p(a)", str(k2s(dev1), PortMappingTechnique.NONE));
		colorToStr.put("p(b)", str(PortMappingTechnique.NONE, k2s(dev2)));
		colorToStr.put("pp", str(k2s(dev1), k2s(dev2)));
		colorToStr.put("ii", new TraversalStrategy(false));
		return colorToStr.get(col);
	}

	private static TraversalStrategy str(PortMappingTechnique t1, PortMappingTechnique t2) {
		return new TraversalStrategy(t1, t2, 0.0);
	}

	public static TraversalStrategy getPaperStrategy(GatewayDevice dev1, GatewayDevice dev2) {
		try {
			String aa = removeDash(GatewayDevice.shorten(dev1.getPortAllocationPolicy().name(), "_"));
			String fa = removeDash(GatewayDevice.shorten(dev1.getPortFilteringPolicy().name(), "_"));
			String ma = removeDash(GatewayDevice.shorten(dev1.getPortMappingPolicy().name(), "_"));
			String ab = removeDash(GatewayDevice.shorten(dev2.getPortAllocationPolicy().name(), "_"));
			String fb = removeDash(GatewayDevice.shorten(dev2.getPortFilteringPolicy().name(), "_"));
			String mb = removeDash(GatewayDevice.shorten(dev2.getPortMappingPolicy().name(), "_"));
			String res = colorme(s2i(fa), s2i(ma), s2i(aa), s2i(fb), s2i(mb), s2i(ab));
			return genStr(res, dev1, dev2);
		} catch (Exception e) {
			System.out.println("e");
			return null;
		}
	}

	static HashMap<String, Integer> s2i;
	static {
		s2i = new HashMap<String, Integer>();
		s2i.put("PP", 0);
		s2i.put("PC", 1);
		s2i.put("R", 2);
		s2i.put("EI", 0);
		s2i.put("HD", 1);
		s2i.put("PD", 2);
	}

	private static int s2i(String x) {
		return s2i.get(x);
	}

	static HashMap<String, String> bg;
	static {
		bg = new HashMap<String, String>();
		bg.put("SHP(A)", "green");
		bg.put("SHP(B)", "green");
		bg.put("SHP(A)", "green");
		bg.put("SHP(B)", "green");
		bg.put("PRP(A)", "yellow");
		bg.put("PRP(B)", "yellow");
		bg.put("PRC(A)", "yellow");
		bg.put("PRC(B)", "yellow");
		bg.put("pp", "cyan");
		bg.put("<font color='white'>ii</font>", "white");
		bg.put("X", "red");
		bg.put("e", "yellow");
		bg.put("u", "white");
		bg.put("PRP(A)-PRP(B)", "yellow");
		bg.put("PRC(A)-PRP(B)", "yellow");
		bg.put("PRP(A)-PRC(B)", "yellow");
		bg.put("PRC(A)-PRC(B)", "yellow");
	}

	private static String bg(String entry) {
		return bg.get(entry);
	}

	static double total = 0.0;
	static double nSHP = 0.0;
	static double nP = 0.0;
	static double nDP = 0.0;
	static double nI = 0.0;

	public static String colorme(int fa, int ma, int aa, int fb, int mb, int ab) {
		total += 1.0;
		@SuppressWarnings("unused")
		int PP = 0, PC = 1, R = 2;
		@SuppressWarnings("unused")
		int EI = 0, HD = 1, PD = 2;
		// Easy ones
		if ((fa == EI && ma < PD) || (fa == EI && ma == PD && fb < PD)) {
			nSHP += 1.0;
			return "SHP(A)";
		}
		if ((fb == EI && mb < PD) || (fb == EI && mb == PD && fa < PD)) {
			nSHP += 1.0;
			return "SHP(B)";
		}
		String sa = (aa == 0 ? "PRP(A)" : "PRC(A)");
		String sb = (ab == 0 ? "PRP(B)" : "PRC(B)");
		// Predictable on one side
		if (aa < R && (fa < PD))
			if ((ma < PD) || (ma == PD && fb < PD)) {
				nP += 1.0;
				return sa;
			}
		if (ab < R && (fb < PD))
			if ((mb < PD) || (mb == PD && fa < PD)) {
				nSHP += 1.0;
				return sa;
			}
		// Double pred.
		if (Math.max(aa, ab) < R) {
			nDP += 1.0;
			return "" + sa + "-" + sb;
		} else {
			nI += 1.0;
			// return "<font color='white'>ii</font>";
			return "X";
		}
		// return "u";
		// if (Math.min(fa, fb) > EI){ // The hard cases
		// //Simult. predict.
		// if ( (fa<=PD) && (ma<=PD) && (ma<=PD) && (mb<=PD) ){
		// if (Math.max(aa,ab)<R)
		// return "p";
		// else
		// return "i";
		// }
		//
		// return "u";
		//
		//
		//
		// }
		// else{//SHP the easy cases
		// //it is in the hard category
		// if (Math.max(aa,ab)<R)
		// return "pp";
		// else
		// return "ii";
		// //return "e";
		// }
	}

	// public static String colorme(int fa, int ma, int aa, int fb,int mb,int ab){
	//
	// int PP=0, PC=1, R=2;
	// int EI=0, HD=1, PD=2;
	//
	// if (Math.min(fa, fb) > EI){ // The hard cases
	// //Simult. predict.
	// if ( (fa<=PD) && (ma<=PD) && (ma<=PD) && (mb<=PD) ){
	// if (Math.max(aa,ab)<R)
	// return "p";
	// else
	// return "i";
	// }
	//
	// return "u";
	//
	//
	//
	// }
	// else{//SHP the easy cases
	// if ((fa==EI && ma<PD) || (fa==EI && ma==PD && fb<PD) ){
	// return "t(a)";
	// }
	// if ((fb==EI && mb<PD) || (fb==EI && mb==PD && fa<PD) ){
	// return "t(b)";
	// }
	// //it is in the hard category
	// if (Math.max(aa,ab)<R)
	// return "pp";
	// else
	// return "ii";
	// //return "e";
	// }
	//
	// }
	public static String fromRealToModelStr(String str) {
		if (str.contains("N"))
			return "G";
		if (str.contains("A"))
			return "H";
		if (str.contains("PP"))
			return "P";
		if (str.contains("PC"))
			return "C";
		return "";
	}

	public static String removeDash(String str) {
		StringTokenizer tok = new StringTokenizer(str, "_");
		if (tok.countTokens() == 2)
			return tok.nextToken() + tok.nextToken();
		return str;
	}
}
